/*
 * Copyright (c) 2020 Huawei Technologies Co.,Ltd.
 *
 * openGauss is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *          http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 *-------------------------------------------------------------------------
 *
 * pairingheap.cpp
 *    A Pairing Heap implementation
 *
 * A pairing heap is a data structure that's useful for implementing
 * priority queues. It is simple to implement, and provides amortized O(1)
 * insert and find-min operations, and amortized O(log n) delete-min.
 *
 * IDENTIFICATION
 *    src/common/backend/lib/pairingheap.cpp
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "lib/pairingheap.h"

#define HEAP_NODE_SWAP(a, b)    \
{                               \
    pairingheap_node *tmp;      \
    tmp = (a);                  \
    (a) = (b);                  \
    (b) = tmp;                  \
}

#define HEAP_NODE_ADD_FIRSTCHILDREN(a, b)        \
{                                                \
    if ((a)->first_child)                        \
        (a)->first_child->prev_or_parent = (b);  \
    (b)->prev_or_parent = (a);                   \
    (b)->next_sibling = (a)->first_child;        \
    (a)->first_child = (b);                      \
}

static pairingheap_node *merge(pairingheap *heap, pairingheap_node *a, pairingheap_node *b);
static pairingheap_node *merge_children(pairingheap *heap, pairingheap_node *children);

/*
 * pairingheap_allocate
 *
 * Returns a pointer to a newly-allocated heap, with the heap property defined
 * by the given comparator function, which will be invoked with the additional
 * argument specified by 'arg'.
 */
pairingheap *
pairingheap_allocate(pairingheap_comparator compare, void *arg)
{
    pairingheap *heap = (pairingheap *) palloc(sizeof(pairingheap));

    heap->ph_compare = compare;
    heap->ph_arg = arg;
    heap->ph_root = NULL;
    return heap;
}

/*
 * pairingheap_free
 *
 * Releases memory used by the given pairingheap.
 *
 * Note: The nodes in the heap are not freed!
 */
void
pairingheap_free(pairingheap *heap)
{
    pfree(heap);
}

/*
 * A helper function to merge two subheaps into one.
 *
 * The subheap with smaller value is put as a child of the other one (assuming
 * a max-heap).
 *
 * The next_sibling and prev_or_parent pointers of the input nodes are
 * ignored. On return, the returned node's next_sibling and prev_or_parent
 * pointers are garbage.
 */
static pairingheap_node *
merge(pairingheap *heap, pairingheap_node *a, pairingheap_node *b)
{
    if (a == NULL)
        return b;
    if (b == NULL)
        return a;

    /* swap 'a' and 'b' so that 'a' is the one with larger value */
    if (heap->ph_compare(a, b, heap->ph_arg) < 0)
        HEAP_NODE_SWAP(a, b);

    /* and put 'b' as a child of 'a' */
    HEAP_NODE_ADD_FIRSTCHILDREN(a, b);
    return a;
}

/*
 * pairingheap_add
 *
 * Adds the given node to the heap in O(1) time.
 */
void
pairingheap_add(pairingheap *heap, pairingheap_node *node)
{
    node->first_child = NULL;

    /* Link the new node as a new tree */
    heap->ph_root = merge(heap, heap->ph_root, node);
    heap->ph_root->prev_or_parent = NULL;
    heap->ph_root->next_sibling = NULL;
}

/*
 * pairingheap_first
 *
 * Returns a pointer to the first (root, topmost) node in the heap without
 * modifying the heap. The caller must ensure that this routine is not used on
 * an empty heap. Always O(1).
 */
pairingheap_node *
pairingheap_first(pairingheap *heap)
{
    Assert(!pairingheap_is_empty(heap));
    return heap->ph_root;
}

/*
 * pairingheap_remove_first
 *
 * Removes the first (root, topmost) node in the heap and returns a pointer to
 * it after rebalancing the heap. The caller must ensure that this routine is
 * not used on an empty heap. O(log n) amortized.
 */
pairingheap_node *
pairingheap_remove_first(pairingheap *heap)
{
    pairingheap_node *result;
    pairingheap_node *children;

    Assert(!pairingheap_is_empty(heap));

    /* Remove the root, and form a new heap of its children. */
    result = heap->ph_root;
    children = result->first_child;

    heap->ph_root = merge_children(heap, children);
    if (heap->ph_root) {
        heap->ph_root->prev_or_parent = NULL;
        heap->ph_root->next_sibling = NULL;
    }

    return result;
}

/*
 * Remove 'node' from the heap. O(log n) amortized.
 */
void
pairingheap_remove(pairingheap *heap, pairingheap_node *node)
{
    pairingheap_node *children;
    pairingheap_node *replacement;
    pairingheap_node *next_sibling;
    pairingheap_node **prev_ptr;

    /*
     * If the removed node happens to be the root node, do it with
     * pairingheap_remove_first().
     */
    if (node == heap->ph_root) {
        (void) pairingheap_remove_first(heap);
        return;
    }

    /*
     * Before we modify anything, remember the removed node's first_child and
     * next_sibling pointers.
     */
    children = node->first_child;
    next_sibling = node->next_sibling;

    /*
     * Also find the pointer to the removed node in its previous sibling, or
     * if this is the first child of its parent, in its parent.
     */
    if (node->prev_or_parent->first_child == node)
        prev_ptr = &node->prev_or_parent->first_child;
    else
        prev_ptr = &node->prev_or_parent->next_sibling;
    Assert(*prev_ptr == node);

    /*
     * If this node has children, make a new subheap of the children and link
     * the subheap in place of the removed node. Otherwise just unlink this
     * node.
     */
    if (children) {
        replacement = merge_children(heap, children);

        replacement->prev_or_parent = node->prev_or_parent;
        replacement->next_sibling = node->next_sibling;
        *prev_ptr = replacement;
        if (next_sibling)
            next_sibling->prev_or_parent = replacement;
    } else {
        *prev_ptr = next_sibling;
        if (next_sibling)
            next_sibling->prev_or_parent = node->prev_or_parent;
    }
}

/*
 * Merge a list of subheaps into a single heap.
 *
 * This implements the basic two-pass merging strategy, first forming pairs
 * from left to right, and then merging the pairs.
 */
static pairingheap_node *
merge_children(pairingheap *heap, pairingheap_node *children)
{
    pairingheap_node *curr, *next;
    pairingheap_node *pairs;
    pairingheap_node *newroot;

    if (children == NULL || children->next_sibling == NULL)
        return children;

    /* Walk the subheaps from left to right, merging in pairs */
    next = children;
    pairs = NULL;
    for (;;) {
        curr = next;

        if (curr == NULL)
            break;

        if (curr->next_sibling == NULL) {
            /* last odd node at the end of list */
            curr->next_sibling = pairs;
            pairs = curr;
            break;
        }

        next = curr->next_sibling->next_sibling;
        /* merge this and the next subheap, and add to 'pairs' list. */
        curr = merge(heap, curr, curr->next_sibling);
        curr->next_sibling = pairs;
        pairs = curr;
    }

    /*
     * Merge all the pairs together to form a single heap.
     */
    newroot = pairs;
    next = pairs->next_sibling;
    while (next) {
        curr = next;
        next = curr->next_sibling;

        newroot = merge(heap, newroot, curr);
    }

    return newroot;
}

/*
 * A debug function to dump the contents of the heap as a string.
 *
 * The 'dumpfunc' callback appends a string representation of a single node
 * to the StringInfo. 'opaque' can be used to pass more information to the
 * callback.
 */
#ifdef PAIRINGHEAP_DEBUG
static void
pairingheap_dump_recurse(StringInfo buf,
                         pairingheap_node *node,
                         void (*dumpfunc) (pairingheap_node *node, StringInfo buf, void *opaque),
                         void *opaque,
                         int depth,
                         pairingheap_node *prev_or_parent)
{
    while (node) {
        Assert(node->prev_or_parent == prev_or_parent);

        appendStringInfoSpaces(buf, depth * 4);
        dumpfunc(node, buf, opaque);
        appendStringInfoChar(buf, '\n');
        if (node->first_child)
            pairingheap_dump_recurse(buf, node->first_child, dumpfunc, opaque, depth + 1, node);
        prev_or_parent = node;
        node = node->next_sibling;
    }
}

char *
pairingheap_dump(pairingheap *heap,
                 void (*dumpfunc) (pairingheap_node *node, StringInfo buf, void *opaque),
                 void *opaque)
{
    StringInfoData buf;

    if (!heap->ph_root)
        return pstrdup("(empty)");

    initStringInfo(&buf);
    pairingheap_dump_recurse(&buf, heap->ph_root, dumpfunc, opaque, 0, NULL);
    return buf.data;
}
#endif
